package com.iflytek.jcy.supervise.job.web;


import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.druid.stat.DruidStatManagerFacade;
import com.alibaba.ttl.threadpool.TtlExecutors;
import com.iflytek.jcy.supervise.datastore.dto.TableInfo;
import com.iflytek.jcy.supervise.job.component.httpinterface.OuterHttpInterface;
import com.iflytek.jcy.supervise.job.service.impl.DataStoreServiceImpl;
import com.yomahub.tlog.core.thread.TLogInheritableTask;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.LongTaskTimer;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.Timer;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @since 2021-08-25
 */
@RestController
@RequestMapping("/test")
public class IndexController {
    protected final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    MeterRegistry registry;
    @Resource
    private OuterHttpInterface outerHttpInterface;

    private Counter counter;
    private AtomicInteger gauge;
    private DistributionSummary summary;
    private LongTaskTimer long_task_timer;
    private DistributionSummary summarySimple;
    @Resource
    private DataStoreServiceImpl dataStoreServiceImpl;

    @PostMapping("ttt")
    public void ttt() {
        List<TableInfo> tables=dataStoreServiceImpl.fetchAllTables();
        System.out.println(tables);
    }

    @PostConstruct
    private void init() {
        // Gauge.builder("lego.thread-pool.pending_task", new AtomicInteger(37), AtomicInteger::get);
        Tags tags = Tags.of("endpoint", "user");
        counter = Counter.builder("counter_user").tag("method", "IndexController.core").register(registry);
        gauge = registry.gauge("guage_user", new AtomicInteger(0));
        summarySimple = registry.summary("summary_user");

        // 客户端计算百分位publishPercentiles 发布直方图publishPercentileHistogram(指标较多, 谨慎开启)
        summary = DistributionSummary.builder("summary_quantile").scale(100).publishPercentiles(0.5, 0.8, 0.95)
                                     .publishPercentileHistogram().register(registry);
        long_task_timer = registry.more().longTaskTimer("long_task_timer_user");
    }

    @GetMapping("/stat")
    public Object druidStat() {
        // 获取Druid数据源的监控数据
        return DruidStatManagerFacade.getInstance().getDataSourceStatDataList();
    }

    ExecutorService pool = new ThreadPoolExecutor(10, 10, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(1000));

    @PostMapping("/test")
    public Object receive(@RequestBody Object body) {
        logger.info("{}", body);
        ExecutorService service = TtlExecutors.getTtlExecutorService(pool);
        service.submit(() -> {
            String s = outerHttpInterface.simpleGet();
            logger.info("simpleGet1: " + StringUtils.length(s));
        });

        pool.submit(new TLogInheritableTask() {
            @Override
            public void runTask() {
                String s = outerHttpInterface.simpleGet();
                logger.info("simpleGet2: " + StringUtils.length(s));
            }
        });
        pool.submit(() -> {
            String s = outerHttpInterface.simpleGet();
            logger.info("simpleGet3: " + StringUtils.length(s));
        });
        return body.toString().length();
    }

    @GetMapping
    public void testMetrics() throws InterruptedException {
        counter.increment();
        gauge.getAndSet(RandomUtils.nextInt(0, 100000));

        Timer.Sample sample = Timer.start(registry);
        // 模拟业务操作
        Thread.sleep(RandomUtils.nextInt(10, 1000));
        sample.stop(Timer.builder("timer_user").description("一段描述").tags("endpoint", "user").publishPercentileHistogram(
                true).publishPercentiles(0.5, 0.75, 0.9).register(registry));


        summarySimple.record(RandomUtils.nextDouble(10, 1000));
        summary.record(RandomUtils.nextDouble(10, 1000));
        LongTaskTimer.Sample sample1 = long_task_timer.start();
        Thread.sleep(RandomUtils.nextInt(10, 1000));
        sample1.stop();
    }

    // 测试登录
    @GetMapping("doLogin")
    public String doLogin(String username, String password) {
        // 此处仅作模拟示例，真实项目需要从数据库中查询数据进行比对
        if ("admin".equals(username) && "123456".equals(password)) {
            StpUtil.login(10001);
            return "登录成功";
        }
        // 标记当前会话登录的账号id
        // 建议的参数类型：long | int | String， 不可以传入复杂类型，如：User、Admin等等
        // StpUtil.login(Object id);

        // 当前会话注销登录
        // StpUtil.logout();

        // 获取当前会话是否已经登录，返回true=已登录，false=未登录
        // StpUtil.isLogin();

        // 检验当前会话是否已经登录, 如果未登录，则抛出异常：`NotLoginException`
        // StpUtil.checkLogin()

        // 获取当前会话账号id, 如果未登录，则抛出异常：`NotLoginException`
        // StpUtil.getLoginId();

        // 类似查询API还有：
        // StpUtil.getLoginIdAsString();    // 获取当前会话账号id, 并转化为`String`类型
        // StpUtil.getLoginIdAsInt();       // 获取当前会话账号id, 并转化为`int`类型
        // StpUtil.getLoginIdAsLong();      // 获取当前会话账号id, 并转化为`long`类型

        // ---------- 指定未登录情形下返回的默认值 ----------

        // 获取当前会话账号id, 如果未登录，则返回null
        // StpUtil.getLoginIdDefaultNull();

        // 获取当前会话账号id, 如果未登录，则返回默认值 （`defaultValue`可以为任意类型）
        // StpUtil.getLoginId(T defaultValue);

        // 获取指定token对应的账号id，如果未登录，则返回 null
        // StpUtil.getLoginIdByToken(String tokenValue);

        // 获取当前`StpLogic`的token名称
        // StpUtil.getTokenName();

        // 获取当前会话的token值
        // StpUtil.getTokenValue();

        // 获取当前会话的token信息参数
        // StpUtil.getTokenInfo();

        /*------------ 权限 -------------*/
        // 自定义权限扩展  com.iflytek.jcy.component.permission.StpInterfaceImpl
        // 判断：当前账号是否含有指定权限, 返回true或false
        // StpUtil.hasPermission("user-update");

        // 校验：当前账号是否含有指定权限, 如果验证未通过，则抛出异常: NotPermissionException
        // StpUtil.checkPermission("user-update");

        // 校验：当前账号是否含有指定权限 [指定多个，必须全部验证通过]
        // StpUtil.checkPermissionAnd("user-update", "user-delete");

        // 校验：当前账号是否含有指定权限 [指定多个，只要其一验证通过即可]
        // StpUtil.checkPermissionOr("user-update", "user-delete");

        // 判断：当前账号是否拥有指定角色, 返回true或false
        // StpUtil.hasRole("super-admin");

        // 校验：当前账号是否含有指定角色标识, 如果验证未通过，则抛出异常: NotRoleException
        // StpUtil.checkRole("super-admin");

        // 校验：当前账号是否含有指定角色标识 [指定多个，必须全部验证通过]
        // StpUtil.checkRoleAnd("super-admin", "shop-admin");

        // 校验：当前账号是否含有指定角色标识 [指定多个，只要其一验证通过即可]
        // StpUtil.checkRoleOr("super-admin", "shop-admin");

        // 当拥有 user* 权限时
        // StpUtil.hasPermission("user-add");        // true
        // StpUtil.hasPermission("user-update");     // true
        // StpUtil.hasPermission("art-add");         // false

        /* ------------ 注销/踢人 ------------- */
        //  强制注销等价于对方主动调用了注销方法，再次访问会提示：Token无效。
        //  踢人下线不会清除Token信息，而是将其打上特定标记，再次访问会提示：Token已被踢下线。

        // 强制注销
        // StpUtil.logout(10001);                    // 强制指定账号注销下线
        // StpUtil.logout(10001, "PC");              // 强制指定账号指定端注销下线
        // StpUtil.logoutByTokenValue("token");      // 强制指定 Token 注销下线
        // 踢人下线
        // StpUtil.kickout(10001);                    // 将指定账号踢下线
        // StpUtil.kickout(10001, "PC");              // 将指定账号指定端踢下线
        // StpUtil.kickoutByTokenValue("token");      // 将指定 Token 踢下线

        /* ------------ 账号封禁 ------------- */
        // 封禁指定账号
        // 参数一：账号id  参数二：封禁时长，单位：秒  (86400秒=1天，此值为-1时，代表永久封禁)
        // StpUtil.disable(10001, 86400);

        // 获取指定账号是否已被封禁 (true=已被封禁, false=未被封禁)
        // StpUtil.isDisable(10001);

        // 获取指定账号剩余封禁时间，单位：秒
        // StpUtil.getDisableTime(10001);

        // 解除封禁
        // StpUtil.untieDisable(10001);

        /* ------------ session ------------- */
        // 在登录时缓存user对象
        // StpUtil.getSession().set("user", user);

        // 然后我们就可以在任意处使用这个user对象
        // SysUser user = (SysUser) StpUtil.getSession().get("user");

        // 获取当前账号id的Session (必须是登录后才能调用)
        // StpUtil.getSession();

        // 获取当前账号id的Session, 并决定在Session尚未创建时，是否新建并返回
        // StpUtil.getSession(true);

        // 获取账号id为10001的Session
        // StpUtil.getSessionByLoginId(10001);

        // 获取账号id为10001的Session, 并决定在Session尚未创建时，是否新建并返回
        // StpUtil.getSessionByLoginId(10001, true);

        // 获取SessionId为xxxx-xxxx的Session, 在Session尚未创建时, 返回null
        // StpUtil.getSessionBySessionId("xxxx-xxxx");

        return "登录失败";
    }

    // 查询登录状态
    @GetMapping("isLogin")
    public String isLogin() {
        return "当前会话是否登录：" + StpUtil.isLogin();
    }// 登录认证：只有登录之后才能进入该方法

    @SaCheckLogin
    @GetMapping("info")
    public String info() {
        return "查询用户信息";
    }

    // 角色认证：必须具有指定角色才能进入该方法
    @SaCheckRole("super-admin")
    @PutMapping("add1")
    public String add1() {
        return "用户增加";
    }

    // 权限认证：必须具有任意权限才能进入该方法 角色权限双重 “or校验”
    @SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR, orRole = "admin")
    @PostMapping("add")
    public String add() {
        return "用户增加";
    }
}
